28장. Reflection
보통 우리는 코드를 이렇게 씁니다.user.name처럼 이름을 직접 적어 값을 꺼냅니다.
그런데 가끔은
“이 객체에 어떤 프로퍼티가 있는지“조차
실행 중에 알아내야 할 때가 있습니다.
이렇게 프로그램이
실행 중에 자기 자신의 구조를 들여다보는 것을
리플렉션(Reflection)이라고 합니다.
비유하자면 이렇습니다.
리플렉션은 프로그램이
거울로 자기 모습을 비춰 보는 것입니다.
“나는 어떤 클래스지? 어떤 함수를 가졌지?”
스프링 같은 프레임워크는
이 리플렉션 위에서 동작합니다.
그 원리를 여기서 엿봅니다.
28.1 Reflection이란
리플렉션을 쓰면
클래스의 이름, 프로퍼티, 함수 등을
실행 중에 알아낼 수 있습니다.
class User(val name: String, val age: Int)
val user = User("홍길동", 20)
println(user::class) // class User
println(user::class.simpleName) // User
::class는 “이 객체의 클래스 정보“를 가져옵니다.
이름을 직접 적지 않고도
클래스 정보를 손에 쥘 수 있습니다.
28.2 Java Reflection과 Kotlin Reflection
리플렉션에는 두 종류가 있습니다.
- 자바 리플렉션 :
Class,Field,Method - 코틀린 리플렉션 :
KClass,KProperty,KFunction
코틀린 리플렉션은
코틀린만의 특징(프로퍼티, null 여부 등)까지
이해합니다.
두 세계를 오갈 수도 있습니다.
val kClass = User::class // 코틀린 KClass
val javaClass = User::class.java // 자바 Class
코틀린 코드에서는
대개 코틀린 리플렉션을 씁니다.
28.3 KClass
KClass는 코틀린에서
“클래스 그 자체“를 나타내는 타입입니다.
::class로 얻습니다.
val kClass = User::class
println(kClass.simpleName) // User
println(kClass.isData) // false (data class 여부)
KClass를 통해
그 클래스의 프로퍼티, 함수, 애너테이션 등
거의 모든 정보에 접근할 수 있습니다.
28.4 Property Reflection
클래스가 가진 프로퍼티들을
목록으로 훑을 수 있습니다.
val user = User("홍길동", 20)
for (prop in User::class.memberProperties) {
println("${prop.name} = ${prop.get(user)}")
}
// name = 홍길동
// age = 20
프로퍼티 이름을 미리 몰라도
모든 값을 꺼낼 수 있습니다.
이런 능력 덕분에
객체를 자동으로 JSON으로 바꾸거나,
로그로 남기는 도구를 만들 수 있습니다.
28.5 Function Reflection
함수도 같은 방식으로 다룰 수 있습니다.
class Greeter {
fun hello(name: String) = "안녕, $name"
}
val greeter = Greeter()
val func = Greeter::hello
println(func.call(greeter, "코틀린")) // 안녕, 코틀린
함수 이름을 문자열이 아닌
실제 참조로 다루고,call로 실행할 수 있습니다.
28.6 Annotation 조회하기
27장에서 붙인 애너테이션은
리플렉션으로 읽어야 활용됩니다.
이때 그 애너테이션은RUNTIME으로 유지되어야 합니다.
@Retention(AnnotationRetention.RUNTIME)
annotation class Role(val name: String)
@Role("ADMIN")
class AdminService
val role = AdminService::class.annotations
.filterIsInstance<Role>()
.firstOrNull()
println(role?.name) // ADMIN
“이 클래스에 어떤 표식이 붙어 있나“를
실행 중에 확인하는 것입니다.
프레임워크는 바로 이 방식으로@Controller, @Service 같은 표식을 찾아냅니다.
28.7 Reflection의 비용과 사용 시 주의점
리플렉션은 강력하지만 공짜가 아닙니다.
- 일반 호출보다 느립니다
- 컴파일 시점의 안전성이 약해집니다 (오타가 나도 실행 중에야 오류가 드러남)
- 코드가 이해하기 어려워질 수 있습니다
그래서 원칙은 이렇습니다.
평범한 코드는 리플렉션 없이 짠다.
리플렉션은 “이름을 미리 알 수 없는” 경우에만 쓴다.
대부분의 일상 코드에서는
리플렉션이 필요 없습니다.
28.8 프레임워크가 객체를 다루는 방식 이해하기
이제 큰 그림이 보입니다.
스프링 같은 프레임워크는
우리가 만든 클래스를 미리 알지 못합니다.
그런데도 우리 객체를 만들고 연결해 줍니다.
어떻게 가능할까요?
바로 리플렉션 덕분입니다.
@Service같은 애너테이션을 리플렉션으로 찾고- 그 클래스의 생성자를 리플렉션으로 확인하고
- 필요한 객체를 만들어 넣어 줍니다
이 과정을 이해하면
“프레임워크가 마법이 아니라
리플렉션이라는 기술 위에 서 있다“는 것을
알게 됩니다.
리플렉션을 직접 쓸 일은 적지만,
이 원리를 알면 프레임워크가 훨씬 덜 신비롭게 보입니다.
28장을 마치며
이 장에서 우리는 다음을 배웠습니다.
- 리플렉션은 실행 중에 자기 구조를 들여다보는 기술이라는 점
KClass로 클래스 정보에 접근하는 법- 프로퍼티와 함수, 애너테이션을 리플렉션으로 읽는 법
- 리플렉션은 비용이 있어 꼭 필요할 때만 쓴다는 점
- 프레임워크가 리플렉션 위에서 동작한다는 원리
이것으로 9부(자바 생태계)를 마칩니다.
다음 10부에서는
프로젝트를 구성하고 테스트하는 법을 배웁니다.